<body></body>
<script>
  let activeEffect = null
  const effectStack = [] //effect栈
  const bucket = new WeakMap()
  const data = {foo: 1, bar: 2}

  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newVal) {
      target[key] = newVal
      trigger(target, key)
    }
  })

  function track(target, key) {
    if (!activeEffect) return
    let depMap = bucket.get(target)
    if (!depMap) bucket.set(target, (depMap = new Map()))
    let deps = depMap.get(key)
    if (!deps) depMap.set(key, (deps = new Set()))

    const effectSet = deps.add(activeEffect)
    activeEffect.deps.push(effectSet)
  }

  function trigger(target, key) {
    let depMap = bucket.get(target)
    if (!depMap) return
    const depsSet = depMap.get(key)

    const effectsToRun = new Set(depsSet) || new Set()
    effectsToRun.forEach(effectFn => {
      if (effectFn?.options?.scheduler) effectFn.options.scheduler(effectFn) //执行调度
      else effectFn()
    })
  }

  function effect(fn, options) {
    // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn

      effectStack.push(effectFn)
      const res = fn()
      effectStack.pop(effectFn)
      activeEffect = effectStack[effectStack.length - 1]
      return res
    }
    effectFn.options = options
    effectFn.deps = [] //反向收集 effectSet的引用
    if (!effectFn?.options?.lazy) effectFn()
    return effectFn
  }
  function cleanup(effectFn) {
    effectFn.deps.forEach(depSet => depSet.delete(effectFn))
    effectFn.deps.length = 0 //遍历所有的 Set集合删除同一个副作用
  }

  const jobQueue = new Set() // 任务队列控制逻辑 // 任务队列（自动去重）
  let isFlushing = false // 队列刷新状态
  const p = Promise.resolve() // 微任务触发器

  function flushJob() {
    if (isFlushing) return
    isFlushing = true
    p.then(() => jobQueue.forEach(job => job())).finally(() => (isFlushing = false))
  }

  function traverse(value, seen = new Set()) {
    if (typeof value !== 'object' || value === null || seen.has(value)) return
    seen.add(value)
    for (const k in value) {
      traverse(value[k], seen)
    }
    return value
  }
  //2、watch 函数接收 3个参数，source 是响应式数据，cb是回调函数,options配置
  function watch(source, cb, options = {}) {
    let getter
    if (typeof source === 'function') getter = source
    else getter = () => traverse(source)

    let oldValue, newValue
    const job = () => {
      newValue = effectFn()
      cb(newValue, oldValue)
      oldValue = newValue
    }

    const effectFn = effect(() => getter(), {
      lazy: true,
      scheduler: job
    })
    if (options.immediate) job()
    else oldValue = effectFn()
  }

  let finalData
  watch(
    obj,
    async () => {
      // 发送并等待网络请求
      const res = await fetch('http://localhost:3000/list')
      // 将请求结果赋值给 data
      finalData = await res.json()
      console.log('finalData', finalData)
    },
    {
      immediate: true
    }
  )

  obj.foo = 1
</script>
